/*
   @Author: Drswith
   @Date: 2021-01-11 18:53:23
 * @LastEditors: Drswith
 * @LastEditTime: 2021-03-19 11:44:33
 * @FilePath: \season2\2.Hardware Software\device-6-2-alarm-optimize\device\device.ino
*/
/*    头文件      */
#include <Wire.h>
#include <BH1750.h>
#include <Arduino.h>
#include <U8x8lib.h>
#include <Ticker.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#include "Button2.h";
#include "DHTesp.h"

/*    引脚号宏定义      */
#define PIN_BEEP      13
#define PIN_LED       2
#define PIN_KEY_LED   14
#define PIN_KEY_BEEP  12
#define PIN_DHT11     0
#define PIN_OLED_SCL  3
#define PIN_OLED_SDA  10

#define Alarm_Itv     0.3   //0.3s
#define Alarm_TEMP    28  //28摄氏度
#define Alarm_HUM     80  //80%
#define Alarm_LIGHT   20  //光照度20Lux

//  if温度高了就报警
//  报警实际上是蜂鸣器或者LED的高低电平转换


/*    全局变量（对象）      */
const char* ssid = "spx24";// wifi名称
const char* password = "52xiaobai999";//  wifi密码
const char* mqtt_server = "broker.emqx.io";// mqtt服务器地址（IP/域名）

const char* pubTopic = "/mynodemcu/smarthome/pub";//  设备上传Topic
const char* subTopic = "/mynodemcu/smarthome/sub";//  设备订阅Topic


unsigned long previousMillis = 0;
const long interval = 2000;
int Pin_state = LOW;

Button2 led_key = Button2(PIN_KEY_LED);
Button2 beep_key = Button2(PIN_KEY_BEEP);
DHTesp dht;

BH1750 lightMeter;


U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ PIN_OLED_SCL, /* data=*/ PIN_OLED_SDA, /* reset=*/ U8X8_PIN_NONE);         // Digispark ATTiny85


Ticker timer_alarm; // 报警切换定时器
int Alarm_Count = 10;//报警积累计数器

WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (128)
char msg[MSG_BUFFER_SIZE];

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

//{"led":1}
//{"beep":0}
// https://arduinojson.org/v5/assistant/

void callback(char* topic, byte* payload, unsigned int length) {
  String cmd = "";
  
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    cmd += (char)payload[i];
  }
  Serial.println(cmd);
  
  const size_t capacity = JSON_OBJECT_SIZE(1) + 10;
  DynamicJsonBuffer jsonBuffer(capacity);

  JsonObject& root = jsonBuffer.parseObject(cmd);
  //root.containsKey("city");
  if(root.containsKey("led")){
    int led = root["led"]; 
    if(led){
      digitalWrite(PIN_LED, LOW);
    }else{
      digitalWrite(PIN_LED, HIGH);
    }
  }
  if(root.containsKey("beep")){
    int beep = root["beep"]; 
    if(beep){
      digitalWrite(PIN_BEEP, LOW);
    }else{
      digitalWrite(PIN_BEEP, HIGH);
      Alarm_Count = 0;
      timer_alarm.detach();
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish(pubTopic, "hello world");
      // ... and resubscribe
      client.subscribe(subTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void isAlarm (float temp, float hum, float light) {
  if (temp >= Alarm_TEMP) {
    timer_alarm.attach(Alarm_Itv, flip_alarm);
  } else if (hum >= Alarm_HUM) {
    timer_alarm.attach(Alarm_Itv, flip_alarm);
  } else if (light <= Alarm_LIGHT) {
    timer_alarm.attach(Alarm_Itv, flip_alarm);
  } else {
    timer_alarm.detach();
    digitalWrite(PIN_BEEP, HIGH);
  }
}

void flip_alarm() {
  digitalWrite(PIN_BEEP, !digitalRead(PIN_BEEP));
}



/*    初始化函数      */
void setup() {
  Serial.begin(115200);
  delay(50);
  Serial.println("\n\nSave Tiro Season2 NodeMcu Arduino IDE Weapp\n\n");

  //  初始化led与beep引脚
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BEEP, OUTPUT);

  digitalWrite(PIN_BEEP, HIGH);
  digitalWrite(PIN_LED, HIGH);

  //  初始化led与beep按键引脚
  led_key.setTapHandler(led_tap);
  beep_key.setTapHandler(beep_tap);

  //  初始化dht11温湿度传感器
  dht.setup(PIN_DHT11, DHTesp::DHT11); // Connect DHT sensor to PIN_DHT11

  //  初始化bh1750光照度传感器
  Wire.begin();
  lightMeter.begin();
  //初始化OLED
  u8x8.begin();
  u8x8.setFont(u8x8_font_8x13_1x2_r);
  u8x8.setPowerSave(0);
  u8x8.drawString(2, 0, "Welcome Home");
  u8x8.drawString(0, 4, "Reading Data..");

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}


/*    主循环函数      */
void loop() {
  unsigned long currentMillis = millis();
  static float temperature;
  static float humidity;
  static float lux;

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    char displaybuf[32];
    memset(displaybuf, 0, sizeof(displaybuf));

    //    获取温湿度数据
    if (dht.getStatusString() == "OK") {
      humidity = dht.getHumidity();
      temperature = dht.getTemperature();
      Serial.print("\t");
      Serial.print(humidity, 1);
      Serial.print("\t\t");
      Serial.print(temperature, 1);
      Serial.print("\t\t");

      sprintf(displaybuf, "Temp: %.1f C ", temperature);
      u8x8.drawString(0, 2, displaybuf);
      sprintf(displaybuf, "Hum: %.1f %%   ", humidity);
      u8x8.drawString(0, 4, displaybuf);
    }
    //  获取光照度数据
    lux = lightMeter.readLightLevel();
    if (lux > 0) {
      Serial.print("Light: ");
      Serial.print(lux);
      Serial.println(" lx");
      sprintf(displaybuf, "Light: %.1f Lx ", lux);
      u8x8.drawString(0, 6, displaybuf);
    }
    
    //报警手动、自动化    
    if(Alarm_Count < 10)Alarm_Count++;
    else{
      isAlarm(temperature, humidity, lux);
    }
    Serial.printf("Alarm_Count = %d\r\n",Alarm_Count);
  
    //上传数据到MQTT服务器
    snprintf (msg, MSG_BUFFER_SIZE, "{\"Temp\":%.1f,\"Hum\":%.1f,\"Light\":%.1f,\"Led\":%d,\"Beep\":%d}", temperature,humidity,lux,digitalRead(PIN_LED),digitalRead(PIN_BEEP));
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish(pubTopic, msg);
  }

  led_key.loop();
  beep_key.loop();
}

void led_tap(Button2& btn) {
  Serial.println("led_key tap");
  digitalWrite(PIN_LED, !digitalRead(PIN_LED));
}
void beep_tap(Button2& btn) {
  Serial.println("beep_key tap");
  Alarm_Count = 0;
  timer_alarm.detach();
  digitalWrite(PIN_BEEP, HIGH);
}
